/*
        Assignment week 3 cg 2010-2011 
        OpenGL: Phong shading with GLSL (3)
        by      Zhe Sun
                Gerard Knap s1222376

        Use r-mouse down + up & down movement of the mouse to zoom in and out.
        Use l-mouse down to rotate arround the cube
        Use wasd on the keyboard for panning and zooming

        most used sources used for this program in random order:
        - http://www.opengl.org/sdk/docs/tutorials/ClockworkCoders/lighting.php
        - http://www.hlc-games.de/forum/viewtopic.php?f=10&t=56
        - http://www.cs.wpi.edu/~emmanuel/courses/cs543/slides/cliff_iintro_glsl_final.pdf
        - http://www.ozone3d.net/tutorials/glsl_lighting_phong.php
        - http://nehe.gamedev.net/data/articles/article.asp?article=21
        - the course book
 */

/*
 * An OpenGL template/framework file for the Computer Graphics course
 * at the University of Groningen.
 */

// If windows is used, windows.h should be included (before gl.h and glu.h)
#if defined(_WIN32)
#include <windows.h>
#endif

// If you use GLUT you do not need to include gl.h and glu.h
// as glut.h guarantees that gl.h and glu.h are properly 
// included.
// So if you do not wish to use  glut.h, uncomment the following lines.
//#include <GL/gl.h>
//#include <GL/glu.h>

#if defined(NEED_GLEW)
#include "glew.h"
#endif

// Apparently Mac OS X puts the GLUT headers somewhere different.
// For windows we use freeglut.
#if defined(__APPLE__)&& defined(__MACH__)
#include <GLUT/glut.h>
#elif defined(_WIN32)
#include <GL/freeglut.h>
#else
#include <GL/glut.h>
#endif

#include <stdlib.h>
#include <stdio.h>


#include <math.h>
#include "glslshaders.h"

#include "glm.h"
#include "lodepng.h"

#define PHONG 1
#define GOOCH 2
//#define SHADING_MODE PHONG
#define SHADING_MODE PHONG

#ifndef M_PI
#define M_PI           3.14159265358979323846
#endif


#define SPHERE_N (20)

#define APERTURE_SAMPLES 1


void idle();
void glmDrawVBO();

//variables used to store mouse-coordinates
GLfloat startDist = 0, panning = 0;
int prevX = 0, prevY = 0, startX = 0, startY = 0;
GLfloat focus = 0.0;

//variables used to change the viewpoint
GLfloat alt = 0, azim = 0, height = 0.5, distance = 0;
int doZoom, doRotate;

GLMmodel* model; // global variable to store the model
GLuint vertexBufferID, indexBufferID, normalBufferID; // ID of VBO

// for texture
GLUquadric *quadric;
GLuint earthTexture, moonTexture, jupiterTexture;

// for animation
GLfloat escapeT;

int numBall = 4;
float sunPosX=0.0, sunPosY=0.0, sunPosZ=0.0;


  


#if !defined true
#define         true   (0==0)
#define         false   (!true)
#endif

GLuint initTexture(char* filename) {
 	   unsigned char* buffer;
 	   unsigned char* image;
 	   size_t buffersize, imagesize;
 	   GLuint texName;
 	   LodePNG_Decoder decoder;

 	   LodePNG_loadFile(&buffer, &buffersize, filename);
 	   LodePNG_Decoder_init(&decoder);
 	   decoder.infoRaw.color.colorType = 6; /* Load image as RGBA */
 	   LodePNG_decode(&decoder, &image, &imagesize, buffer, buffersize);
 	   if(decoder.error) {
 	      printf("Error reading in png image: %d\n", decoder.error);
 	      exit(1);		
 	   } else {
 	      glPixelStorei(GL_UNPACK_ALIGNMENT,1);
 	      glGenTextures(1,&texName);
 	      glBindTexture(GL_TEXTURE_2D,texName);
 	      glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
 	      glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
 	      glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
 	      glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
 	      glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,decoder.infoPng.width,
 	                   decoder.infoPng.height,0, GL_RGBA,GL_UNSIGNED_BYTE,image);
 	   } 
 	   return texName;
}

void setGoochParameters(){
  GLint loc1, loc2, loc3, loc4;
  GLint p;

  GLfloat alpha[] = {0.25, 0.25, 0.25, 1.0};
  GLfloat beta[] = {0.5, 0.5, 0.5, 1.0};

  GLfloat kBlue[] = {0.0, 0.0, 0.55, 1.0};
  GLfloat kYellow[] = {0.3, 0.3, 0.0, 1.0};
  
  p = glCreateProgram();

  loc1 = glGetUniformLocation(p, "kBlue");
  glUniform4fv(loc1, 1, kBlue);

  loc2 = glGetUniformLocation(p, "kYellow");
  glUniform4fv(loc2, 2, kYellow);

  loc3 = glGetUniformLocation(p, "alpha");
  glUniform4fv(loc3, 3, alpha);

  loc4 = glGetUniformLocation(p, "beta");
  glUniform4fv(loc4, 4, beta);
}

void setGlMaterial(GLfloat r, GLfloat g, GLfloat b, GLfloat ka, GLfloat kd, GLfloat ks, GLfloat n) {
    GLfloat ambient[] = {ka*r, ka*g, ka*b, 1.0};
    GLfloat diffuse[] = {kd*r, kd*g, kd*b, 1.0};
    GLfloat specular[] = {ks, ks, ks, 1.0};
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, ambient);
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse);
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);
    glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, n);
}


void setGlMaterialModel(GLMmaterial m) {
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, m.ambient);
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, m.diffuse);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, m.specular);
	glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, m.shininess);
}


void placeModel() {
	GLMmaterial m;
	int idx;

	idx = model->nummaterials >= 5 ? 4 : 0; // idx=4 is the skin
	m = model->materials[idx]; 
	
	setGlMaterialModel(m);
	//setGlMaterial(1.0f, 1.0f, 0.4f, 0.2, 0.7, 0.5, 2);	

	//glmDraw(model, GLM_SMOOTH);	
	glmDrawVBO();
}

#define GRAVITY 2

float ballPos[4][3] = {{150, 400, 200},{210, 270, 300},{290, 170, 150},{140, 220, 400}};
float ballR[4] = {20, 30,45, 60};
float speed[4] = {1.0, 2.0, 3.0, 0.5};
float lowThresh[4] = {30.0,30, 30, 30};
float upThresh[4] = {400.0,270, 170, 220};
float gravity[4] = {GRAVITY*1.5, GRAVITY, GRAVITY*0.7, GRAVITY*0.7};
int direction[4] = {1,1, 1, 1};
float startTime[4] = {0,0,0,0};
GLuint ballTexture[4] = {0,0,0, 0};
float currTime;

#if SHADING_MODE == PHONG
void placeSpehers() {
    int i;
    float height, t;

    // earth
    glEnable(GL_TEXTURE_2D);
    glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
    
    for (i=0; i<3; i++){
      //if (ballTexture[i] != 0) {
        glBindTexture(GL_TEXTURE_2D,ballTexture[i]);
      //} else {
        //setGlMaterial(0.5f, 0.5f, 0.5f, 0.2, 0.7, 0.8, 32);
      //}
      glPushMatrix();
      glScaled(1,1,-1);

      t = currTime - startTime[i];
      if (direction[i] == 1){
        height = upThresh[i] - 0.5 * gravity[i] * t * t;
        if (height - ballR[i] < lowThresh[i]) {
          direction[i] = 0;
          startTime[i] = currTime;
          speed[i] = gravity[i] * t;
          height = lowThresh[i] + ballR[i];
        }
      } else {
        height = lowThresh[i] + (speed[i] * t - 0.5 * gravity[i] * t * t);
        if (t > (speed[i]/gravity[i])) {
          direction[i] = 1;
          startTime[i] = currTime;
          height = upThresh[i];
        }
      }
      //ballPos[i][1] = height;

      glTranslated(ballPos[i][0], height, ballPos[i][2]);
      gluSphere(quadric,ballR[i], SPHERE_N, SPHERE_N);
      glPopMatrix();
    }
}

#else
void placeSpehers() {
    setGlMaterial(0.4f, 0.4f, 0.4f, 0.2, 0.8, 0.0, 1);
    glPushMatrix();
    glTranslated(200, 200, -1000);
    glutSolidSphere(1000, SPHERE_N, SPHERE_N);
    glPopMatrix();

    setGlMaterial(0.0f, 0.0f, 1.0f, 0.2, 0.7, 0.5, 64);
    glPushMatrix();
    glTranslated(90, 320, 100);
    glutSolidSphere(50, SPHERE_N, SPHERE_N);
    glPopMatrix();

    setGlMaterial(0.0f, 1.0f, 0.0f, 0.2, 0.3, 0.5, 8);
    glPushMatrix();
    glTranslated(210, 270, 300);
    glutSolidSphere(50, SPHERE_N, SPHERE_N);
    glPopMatrix();

    setGlMaterial(1.0f, 0.0f, 0.0f, 0.2, 0.7, 0.8, 32);
    glPushMatrix();
    glTranslated(290, 170, 150);
    glutSolidSphere(50, SPHERE_N, SPHERE_N);
    glPopMatrix();

    setGlMaterial(1.0f, 0.8f, 0.0f, 0.2, 0.8, 0.0, 1);
    glPushMatrix();
    glTranslated(140, 220, 400);
    glutSolidSphere(50, SPHERE_N, SPHERE_N);
    glPopMatrix();

    setGlMaterial(1.0f, 0.5f, 0.0f, 0.2, 0.8, 0.5, 32);
    glPushMatrix();
    glTranslated(110, 130, 200);
    glutSolidSphere(50, SPHERE_N, SPHERE_N);
    glPopMatrix();
}
#endif

void placeLights() {
    GLfloat onesQuad[] = {1.0, 1.0, 1.0, 1.0};
    GLfloat col[] = {0.0, 0.0, 0.0, 1.0};
#if SHADING_MODE == PHONG
    //GLfloat light_position[] = {-1000.0, 600.0, 500.0, 0.0};
    
      GLfloat light_position[] = {-200,600,1500, 0.0};
#else
    GLfloat light_position[] = {-1000, 600, 400};
#endif
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glShadeModel(GL_SMOOTH);


    glMaterialfv(GL_LIGHT0, GL_SPECULAR, onesQuad);
    glMaterialfv(GL_LIGHT0, GL_DIFFUSE, onesQuad);
    glMaterialfv(GL_LIGHT0, GL_AMBIENT, onesQuad);
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT,onesQuad);

    glLightfv(GL_LIGHT0, GL_POSITION, light_position);

}

void mouse(int btn, int state, int x, int y) {
    startX = x;
    startY = y;
    prevX = x;
    prevY = y;

    startDist = distance;

    if (btn == GLUT_LEFT_BUTTON) { // the l-mouse-button is voor rotating arround the cube
        doRotate = state == GLUT_DOWN;
        doZoom = false;
    }
    if (btn == GLUT_RIGHT_BUTTON) { // the r-mouse-button is voor zooming
        doRotate = false;
        doZoom = state == GLUT_DOWN;
    }
    if (btn == GLUT_MIDDLE_BUTTON && state == GLUT_DOWN) { // close on middle click
        exit(0);
    }

    //printf("x:%d, y%d",x, y);
}

// if the user moves the mouse... calculate how much is moved and depening if the user is zooming or rotating
//change the parameters accordingly. This function also updates prevX and prevY.

void motion(int x, int y) {
    int dx, dy;

    dx = startX - x;
    dy = startY - y;

    if (doZoom) {
        distance = (2 * dy) + startDist;
    } else if (doRotate) {
        azim -= 0.5 * (prevX - x);
        alt -= 0.2 * (prevY - y);
    }

    prevX = x;
    prevY = y;
    //printf("x: %d, y:%d, dx: %d, dy:%d\n" , x, y,dx,dy);
}

//if parameters like azim or alt are changed, this function wil redraw the cube with the specified params
// use eyeX eyeY to implement the depth of field

void setViewPoint(GLdouble eyeX, GLdouble eyeY) {
    glLoadIdentity();
    if (true) {
        gluLookAt(eyeX + 200.0, eyeY + 200.0, 800 + distance, 200.0 + panning, 200.0, 0.0, 0.0, 1.0, 0.0);
    } else {
        gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
    }
    glRotatef(alt, 1, 0, 0);
    glRotatef(azim, 0, 1, 0);
}

// focus is distance from eye to plane in focus. 
// Use glFrustum to implement depth of the field

void setViewPoint_withFocus(GLdouble eyedx, GLdouble eyedy) {
    GLdouble fovy, fovy2;
    GLdouble left, right, bottom, top;
    GLdouble dx, dy;
    GLint viewport[4];

    GLdouble currFocus = 200 + focus;

    // the same configuration as reshape
    GLdouble nearView = 300;
    GLdouble farView = 2000;

    // get h and w
    glGetIntegerv(GL_VIEWPORT, viewport);

    // get t/b/l/r from fovy, near and far
    fovy = atan2(viewport[3] / 2.0, farView);

    top = nearView / (cos(fovy) / sin(fovy));
    bottom = -top;

    right = top * ((GLdouble) viewport[2] / (GLdouble) viewport[3]);
    left = -right;

    // get the offset of the view
    dx = eyedx * nearView / currFocus;
    dy = eyedy * nearView / currFocus;

    // adjust the view
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glFrustum(left + dx, right + dx, bottom + dy, top + dy, nearView, farView);

    // adjust the object
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef(-eyedx, -eyedy, 0.0);

    // set eye position to implement zoom in/out and left/right shift
    glLoadIdentity();
    gluLookAt(200.0, 200.0, 1000.0 + distance, 200.0 + panning, 200.0, 0.0, 0.0, 1.0, 0.0);

    // implement mouse rotation by glRotate
    glRotatef(alt, 1, 0, 0);
    glRotatef(azim, 0, 1, 0);


}

void fermat(int n, int c, GLdouble *eyeX, GLdouble *eyeY) {
    GLdouble r, theta;

    if (!eyeX || !eyeY)
        return;

    /*
    theta = (n/2) * 137.508 * M_PI / 180.0;
    if ( n % 2 == 0) {
      r = c * sqrt(n);
    } else {
      r = - c * sqrt(n);
    }
     */

    // only positive r is enough
    r = c * sqrt(n);
    theta = n * 137.508 * M_PI / 180.0;

    *eyeX = r * cos(theta);
    *eyeY = r * sin(theta);

    return;
}

struct {
    GLdouble x;
    GLdouble y;
} jitter8[] = {
    {0.4166666666, 0.625},
    {0.9166666666, 0.875},
    {0.25, 0.375},
    {0.4166666666, 0.125},
    {0.75, 0.125},
    {0.0833333333, 0.125},
    {0.75, 0.625},
    {0.25, 0.875},
    {0.5833333333, 0.375},
    {0.9166666666, 0.375},
    {0.0833333333, 0.625},
    {0.583333333, 0.875}
};

// Depth of field

void display2(void) {
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
	glmDrawVBO();
}

void display(GLfloat currTime) {
    int i;
    GLdouble eyeX = 0.0, eyeY = 0.0;

    glClear(GL_ACCUM_BUFFER_BIT);

    for (i = 0; i < APERTURE_SAMPLES ; i++) {
        /* Clear all pixels */
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

#if 0
        // all view is blur, not really world
        // use scale constance as 1
        fermat(i, 1, &eyeX, &eyeY);
        setViewPoint(0, 0);
#else
        // Use glFrustum to implement to focus and depth of field
#if 0
        eyeX = jitter8[i].x;
        eyeY = jitter8[i].y;
#else
        eyeX = 0;
        eyeY = 0;
#endif
        setViewPoint_withFocus(eyeX, eyeY);
#endif

        placeLights();

#if SHADING_MODE == PHONG

#else
    setGoochParameters();
#endif

		if(false) {
			glmDrawVBO();
		} else {
			//placeModel();
			placeSpehers(currTime);
		}


        glAccum(GL_ACCUM, 1.0 / APERTURE_SAMPLES);
    }

    glAccum(GL_RETURN, 1);
    //glFlush();
    glutSwapBuffers();
}

void displayGooch(void) {


}


void keyboard(unsigned char key, int x, int y) {
    switch (key) {
        case 'q':
        case 'Q':
        case 27: // ESC key
            printf("Exiting...\n");
            exit(0);
            break;
        case 'a':
            panning += 20.0f;
            break;
        case 'd':
            panning -= 20.0f;
            break;
        case 's':
            distance += 20.0f;
            break;
        case 'w':
            distance -= 20.0f;
            break;
        case 'u':
            focus += 20.0f;
            break;
        case 'i':
            focus -= 20.0f;
            break;
    }
}

void reshape(int w, int h) {
    glViewport(0, 0, (GLsizei) w, (GLsizei) h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (true) {
        gluPerspective(2.0 * atan2(h / 2.0, 2000.0)*180.0 / M_PI, (GLdouble) w / (GLdouble) h, 400, 2000);
    } else {
        gluPerspective(60.0, (GLdouble) w / (GLdouble) h, 1.5, 20.0);
    }
    glMatrixMode(GL_MODELVIEW);
}

void OnTimer(int value)
{
   currTime = glutGet(GLUT_ELAPSED_TIME)/500;
   glutPostRedisplay();
   glutTimerFunc(32, OnTimer, 1);
}

void idle() {
  //float currTime;
  //float static fLastIdleTime=0;

  //currTime = glutGet(GLUT_ELAPSED_TIME)/200;
  //fSimTime=fTime-fLastIdleTime;
  
  //fLastIdleTime=fTime;
  //glutPostRedisplay();  
  //display(currTime);
}

void glmInitVBO() {
	// generate a new VBO and get the associated ID
	int i, k, sizeV, sizeN, sizeI, idx;
	int tIdx, vIdx, cIdx;
	//GLfloat* vertices;
	GLuint* indices;	
	GLfloat* normals;

	sizeI = sizeof(GLuint) * model->numtriangles * 3;
	sizeV = sizeof(GLfloat) * model->numvertices * 3 ;
	sizeN = sizeof(GLfloat) * model->numtriangles * 3;
	

	indices = (void *) malloc(sizeI); 

	for(i=0; i< model->numtriangles; i++) {
		indices[i*3] = model->triangles[i].vindices[0];
		indices[i*3+1] = model->triangles[i].vindices[1];
		indices[i*3+2] = model->triangles[i].vindices[2];
	}
	
	normals = (void *) malloc(sizeN); // copy the normals in a way the indices from the vertices can be reused
	for(i=0; i<model->numtriangles; i++) {

		if(true) {
			normals[indices[i*3]] =  model->normals[model->triangles[i].nindices[0]];
			normals[indices[i*3+1]] =  model->normals[model->triangles[i].nindices[1]];
			normals[indices[i*3+2]] = model->normals[model->triangles[i].nindices[2]];
		} else {
			normals[indices[i*3]] =  1;
			normals[indices[i*3+1]] =  1;
			normals[indices[i*3+2]] = 1;
		}
	}

	glGenBuffersARB(1, &vertexBufferID);
	glBindBufferARB(GL_ARRAY_BUFFER_ARB, vertexBufferID);
	glBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeV , model->vertices, GL_STATIC_DRAW_ARB);
		
	glGenBuffersARB(1, &normalBufferID);
	glBindBufferARB(GL_ARRAY_BUFFER_ARB, normalBufferID);
	glBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeN , normals, GL_STATIC_DRAW_ARB); 

    glGenBuffersARB(1, &indexBufferID);
    glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, indexBufferID);
    glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, sizeI, indices, GL_STATIC_DRAW_ARB);
	
}


void glmDrawVBO() {
	glBindBufferARB(GL_ARRAY_BUFFER_ARB, vertexBufferID);
	glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, indexBufferID);

	glEnableClientState(GL_NORMAL_ARRAY);
	glEnableClientState(GL_VERTEX_ARRAY); 	
	
	glVertexPointer(3, GL_FLOAT, 0, 0);  

	glBindBufferARB(GL_ARRAY_BUFFER_ARB, normalBufferID);
	glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, indexBufferID);
	glNormalPointer(GL_FLOAT, 0, 0);

	glDrawElements(GL_TRIANGLES, model->numtriangles*3, GL_UNSIGNED_INT, 0);			
	
	glDisableClientState(GL_VERTEX_ARRAY); 
	glDisableClientState(GL_NORMAL_ARRAY);

	// bind with 0, so, switch back to normal pointer operation
	glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
	glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
}

void readModel(char* filename) {

	model = glmReadOBJ(filename);
	glmFacetNormals(model);
	glmVertexNormals(model, 90);
	glmUnitize(model);
	glmScale(model, 2.0);
}

int main(int argc, char** argv) {
#if defined(NEED_GLEW)
    GLenum err;
#endif

	//read model
	readModel("devilduk.obj");
	
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH | GLUT_ACCUM);
    glutInitWindowSize(400, 400);
    glutInitWindowPosition(220, 100);
    glutCreateWindow("Computer Graphics - OpenGL framework");

#if defined(NEED_GLEW)
    /* Init GLEW if needed */
    err = glewInit();
    if (GLEW_OK != err) {
        /* Problem: glewInit failed, something is seriously wrong. */
        fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
        exit(1);
    }
    fprintf(stdout, "Status: Using GLEW %s\n", glewGetString(GLEW_VERSION));
#endif

    /* Select clearing (background) color */
    glClearColor(0.0, 0.0, 0.0, 0.0);
    //glShadeModel(GL_SMOOTH);
    // for texture, disable shader
    glShadeModel(GL_SMOOTH);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_DEPTH_TEST);

#if SHADING_MODE == PHONG
    //initGLSLProgram("vertexshader.glsl", "fragmentshader.glsl");
#else
    initGLSLProgram("vertexshaderGooch.glsl", "fragmentshaderGooch.glsl");
#endif
    /* Register GLUT callback functions */

    // for texture
    earthTexture = initTexture("earth.png");
    moonTexture = initTexture("moon.png");
    jupiterTexture = initTexture("jupiter.png");

    ballTexture[0] = earthTexture;
    ballTexture[1] = moonTexture;
    ballTexture[2] = jupiterTexture;
    ballTexture[3] = earthTexture;

    quadric = gluNewQuadric();
    gluQuadricDrawStyle(quadric, GLU_FILL);
    gluQuadricOrientation(quadric, GLU_OUTSIDE);
    gluQuadricNormals(quadric, GLU_SMOOTH);
    gluQuadricTexture(quadric, GL_TRUE);

    // makes sure specular highlights do not use the texture color
    glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL,GL_SEPARATE_SPECULAR_COLOR);

    glutDisplayFunc(display); 
    glutKeyboardFunc(keyboard);
    glutReshapeFunc(reshape);
    glutTimerFunc(32, OnTimer, 1);

    glutIdleFunc(idle);
    glutMouseFunc(mouse);
    glutMotionFunc(motion);

	//glmInitVBO();
    glutMainLoop();

    return 0;
}
